﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace ProjectTW.Foliage
{
    /// <summary>
    /// An utility class for batching items.
    /// </summary>
    public static class FoliageMeshUtility
    {
        private static Vector3 s_UpNormal = new Vector3(0, 1, 0);
        
        /// <summary>
        /// Create a new mesh instace
        /// </summary>
        /// <returns></returns>
        public static Mesh CreateNewMesh()
        {
            Mesh mesh = new Mesh();
            mesh.hideFlags = HideFlags.HideAndDontSave;

            mesh.bounds = new Bounds(FoliageManager.FoliageInstanceAreaBoundsMin, FoliageManager.FoliageInstanceAreaBoundsMax);

            return mesh;
        }

        /// <summary>
        /// 合并Mesh接口。
        /// </summary>
        /// <param name="instances"></param>
        /// <param name="mesh"></param>
        /// <param name="meshData"></param>
        public static void CombineMeshes(List<UNCombineInstance> instances, Mesh mesh, FoliageMeshData meshData)
        {
            if (instances.Count == 0)
            {
                mesh.Clear();
                return;
            }

            int instancesCount = instances.Count;

            Vector3[] vertices = new Vector3[meshData.VerticesLength * instancesCount];
            Vector3[] normals = new Vector3[meshData.NormalsLength * instancesCount];
            
            Vector2[] uv0s = new Vector2[meshData.UvLength * instancesCount];
            Vector2[] uv1s = new Vector2[meshData.VerticesLength * instancesCount];
            Vector2[] uv2s = new Vector2[meshData.VerticesLength * instancesCount];
            Vector2[] uv3s = new Vector2[meshData.VerticesLength * instancesCount];
            
            int[] subMeshes = new int[meshData.TrianglesLength * instancesCount];

            int verticesOffset = 0;
            int normalsOffset = 0;
            int uv1sOffset = 0;
            int uv2sOffset = 0;
            int subMeshesOffset = 0;

            for (int i = 0; i < instances.Count; i++)
            {
                MergeMesh(instances[i], i, vertices, normals, uv0s, uv1s, uv2s, uv3s, subMeshes, meshData, verticesOffset, normalsOffset, uv1sOffset, uv2sOffset, subMeshesOffset);

                verticesOffset += meshData.VerticesLength;
                normalsOffset += meshData.NormalsLength;
                uv1sOffset += meshData.UvLength;
                uv2sOffset += meshData.VerticesLength;
                subMeshesOffset += meshData.TrianglesLength;
            }

            mesh.Clear();

            mesh.vertices = vertices;
            mesh.normals = normals;

            mesh.uv = uv0s;
            mesh.uv2 = uv1s;
            mesh.uv3 = uv2s;
            mesh.uv4 = uv3s;

            mesh.SetTriangles(subMeshes, 0);

            mesh.RecalculateBounds();

            mesh.bounds = new Bounds(mesh.bounds.center, new Vector3(Mathf.Clamp(mesh.bounds.size.x, 1, int.MaxValue), 0, Mathf.Clamp(mesh.bounds.size.z, 1, int.MaxValue)));
        }

        /// <summary>
        /// 合并模型，特别注意参数及实现方式。
        /// </summary>
        /// <param name="batchInstance"></param>
        /// <param name="id"></param>
        /// <param name="vertices"></param>
        /// <param name="normals"></param>
        /// <param name="uv0s"></param>
        /// <param name="uv1s"></param>
        /// <param name="uv2s"></param>
        /// <param name="uv3s"></param>
        /// <param name="subMeshes"></param>
        /// <param name="meshData"></param>
        /// <param name="verticesOffset"></param>
        /// <param name="normalsOffset"></param>
        /// <param name="uv1sOffset"></param>
        /// <param name="uv2sOffset"></param>
        /// <param name="subMeshesOffset"></param>
        private static void MergeMesh(UNCombineInstance batchInstance, int id, Vector3[] vertices, Vector3[] normals, Vector2[] uv0s, Vector2[] uv1s, Vector2[] uv2s, Vector2[] uv3s, int[] subMeshes, FoliageMeshData meshData, int verticesOffset, int normalsOffset, int uv1sOffset, int uv2sOffset, int subMeshesOffset)
        {
            Vector3 centerMesh = batchInstance.Transform.MultiplyPoint3x4(Vector3.zero);

            for (int i = 0; i < meshData.VerticesLength; i++)
            {
                vertices[verticesOffset + i] = centerMesh;
            }
            
            // for (int i = 0; i < meshData.verticesLength; i++)
            // {
            //     vertices[verticesOffset + i] = batchInstance.Transform.MultiplyPoint3x4(batchInstance.Mesh.vertices[i]) + batchInstance.Transform.MultiplyPoint3x4(new Vector3(batchInstance.DensityOffset.x, 0, batchInstance.DensityOffset.y));
            // }
            
            for (int i = 0; i < meshData.NormalsLength; i++)
            {
                normals[normalsOffset + i] = s_UpNormal;
            }

            for (int i = 0; i < meshData.UvLength; i++)
            {
                uv0s[uv1sOffset + i] = meshData.Uv[i];
            }

            // Add Centers
            for (int i = 0; i < meshData.VerticesLength; i++)
            {
                // 记录模型顶点X和Y
                uv1s[uv2sOffset + i] = new Vector2(meshData.Vertices[i].x, meshData.Vertices[i].y);
                // UV3记录当前密度对象偏移
                uv2s[uv2sOffset + i] = batchInstance.DensityOffset;
                // UV4的Y值记录当前顶点密度索引， X记录模型Z
                uv3s[uv2sOffset + i] = new Vector2(meshData.Vertices[i].z, batchInstance.DensityIndex);
            }

            for (int t = 0; t < meshData.TrianglesLength; t++)
            {
                subMeshes[subMeshesOffset + t] = meshData.Triangles[t] + verticesOffset;
            }
        }
    }

    /// <summary>
    /// Mesh合并实例。
    /// </summary>
    public struct UNCombineInstance
    {
        public Matrix4x4 Transform;
        public Mesh Mesh;
        public Vector2 DensityOffset;
        public int DensityIndex;
        public float Spread;

        public UNCombineInstance(Matrix4x4 transform, Mesh mesh, float spread, int densityIndex, int id, int x, int z, bool isFlag = true)
        {
            Transform = transform;
            Mesh = mesh;
            Spread = spread;

            DensityIndex = densityIndex;

            if (isFlag)
            {
                Random.InitState(densityIndex * 1000 * (id + 1) + (x + 1) * (z + 1)); 
            }
            else
            {
                Random.InitState(densityIndex * 1000 * (id + 1));
            }

            DensityOffset.x = Random.Range(-1f, 1f);
            DensityOffset.y = Random.Range(-1f, 1f);
        }
    }
}
